﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using OAuth;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;
using System.Xml.Serialization;
using System.Xml;
using System.Text.RegularExpressions;
using System.Web;

namespace PBKVideo
{
    

    public partial class SearchBar : UserControl
    {
        #region constructor
        public SearchBar()
        {
            //WebRequest.DefaultWebProxy = new WebProxy("5.254.101.120", 80);
            InitializeComponent();
            this.textSearch.Font = placeholderFontSearch;
            hideDrop();
            hideSuggestion();
            loading.BringToFront();
            loading.Hide();

            lastquery = null;
        }
        #endregion

        #region enum
        public enum SearchStats
        {
            OK,
            TIMEOUT,
            EMPTY_RESULT,
            NO_MORE_RESULTS,
            NO_INTERNET_CONNECTION,
            UNKNOWN_INTERNET_CONNECTION_ERROR,
            UNKNOWN_ERROR
        };

        public enum VideoHosts
        {
            YOUTUBE,
            VIMEO,
            DAILYMOTION,
            VEOH
        };
        #endregion

        #region private variable

        private bool showListDrop = true;
        private bool showListSuggestion = true;
        private bool suggSuccess = false;
        private int swSugg = 0;
        private VideoHosts selectedVideoHost = VideoHosts.YOUTUBE;

        private readonly Font defaultFontSearch = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        private readonly Font placeholderFontSearch = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        private string searchPlaceholder = @"search query...";

        private System.Drawing.Bitmap[] listLogo = {
                                                       global::PBKVideo.Properties.Resources.youtube,
                                                       global::PBKVideo.Properties.Resources.vimeo,
                                                       global::PBKVideo.Properties.Resources.dailymotion,
                                                       global::PBKVideo.Properties.Resources.veoh
                                                   };
        private String suggestUrl = @"http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&q=";

        //0: query , 1: perpage , 2: page
        private readonly int perpage = 10;
        private int curpage;
        private string lastquery;
        private readonly string vimeoURI = @"http://vimeo.com/api/rest/v2?format=json&method=vimeo.videos.search&query={0}&per_page={1}&page={2}&sort=relevant&full_response=1";
        private readonly string youtubeURI = @"https://gdata.youtube.com/feeds/api/videos?orderby=relevance&q={0}&max-results={1}&start-index={2}&v=2&format=5&alt=jsonc";
        private readonly string dailymotionURI = @"https://api.dailymotion.com/videos?fields=description,id%2Cowner%2Cthumbnail_180_url%2Ctitle&search={0}&sort=relevance&limit={1}&page={2}";
        private readonly string veohURI = @"http://www.veoh.com/rest/v2/execute.xml?method=veoh.search.video&sort=most+viewed&userQuery={0}&safe=false&maxResults={1}&offset={2}&apiKey=5697781E-1C60-663B-FFD8-9B49D2B56D36";

        //this.textSearch.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0)));

        private readonly int SUGGESTION_TIMEOUT = 2000;
        private bool IsSearching = false;
        private bool IsSuggesting = false;
        private RegisteredWaitHandle reqeustWaitHandle;

        private delegate void ProcessSuggestingFinishedCallback(string res);

        private FileDownloader downloader;

        #endregion

        #region trigger search
        private void textSearch_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Down)
            {
                if (showListSuggestion)
                    textSearch_TextChanged(sender, e);
                else
                {
                    if (suggSuccess)
                    {
                        if (listSuggestion.Items.Count > 0)
                        {
                            listSuggestion.Focus();
                            listSuggestion.SetSelected(0, true);
                        }
                    }
                }
            }
            else
            {
                if (e.KeyCode == Keys.Enter)
                {
                    hideSuggestion();
                    //GetSearchResultOnPage(1);
                    StartSearch();
                }
                else
                    if (e.KeyCode == Keys.Tab)
                    {
                        // do nothing...
                        // ternyata ga do nothing... wkwkwk...
                    }
                    else
                        if (e.KeyCode == Keys.Escape)
                        {
                            hideSuggestion();
                        }
            }
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            //GetSearchResultOnPage(1);
            StartSearch();
        }
        #endregion

        #region handling user behavior
        private void listSuggestion_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                swSugg = 1;
                ListBox list = sender as ListBox;
                if (list.SelectedItem != null)
                {
                    textSearch.Text = list.SelectedItem.ToString();
                    textSearch.Select(textSearch.Text.Length, 0);
                    hideSuggestion();
                }
            }
            else
                if (e.KeyChar >= 32)
                {
                    textSearch.Text += e.KeyChar;
                    textSearch.Select(textSearch.Text.Length, 0);
                    textSearch.Focus();
                }
                else
                    if (e.KeyChar == 8)
                    {
                        textSearch.Text = textSearch.Text.Remove(textSearch.Text.Length - 1);
                        textSearch.Select(textSearch.Text.Length, 0);
                        textSearch.Focus();
                    }
        }

        private void listSuggestion_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            listSuggestion_KeyPress(sender, new KeyPressEventArgs((char)13));
        }

        private void panelDropdown_MouseDown(object sender, MouseEventArgs e)
        {
            panelDropdown.BackColor = Color.Turquoise;
        }

        private void panelDropdown_MouseUp(object sender, MouseEventArgs e)
        {
            panelDropdown.BackColor = Color.Transparent;
            if (showListDrop)
            {
                showDrop();
                hideSuggestion();
            }
            else
            {
                hideDrop();
            }
        }

        private void panelDropdown_MouseHover(object sender, EventArgs e)
        {
            this.Cursor = Cursors.Hand;
        }

        private void panelDropdown_MouseLeave(object sender, EventArgs e)
        {
            this.Cursor = Cursors.Default;
        }

        private void textSearch_Leave(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(textSearch.Text))
            {
                textSearch.Font = placeholderFontSearch;
                textSearch.Text = searchPlaceholder;
            }
        }

        private void textSearch_Enter(object sender, EventArgs e)
        {
            if (textSearch.Font.Style == FontStyle.Italic)
            {
                textSearch.Font = defaultFontSearch;
                textSearch.Text = "";
            }
        }

        private void listSuggestion_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                hideSuggestion();
            }
        }

        private void panelDropdown_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                hideDrop();
                hideSuggestion();
            }
        }

        private void btnSearch_MouseHover(object sender, EventArgs e)
        {
            Cursor = Cursors.Hand;
        }

        private void btnSearch_MouseLeave(object sender, EventArgs e)
        {
            Cursor = Cursors.Default;
        }

        private void showDrop()
        {
            showListDrop = false;
            listVideoHost.Show();
        }

        private void hideDrop()
        {
            showListDrop = true;
            listVideoHost.Hide();
        }

        private void showSuggestion()
        {
            showListSuggestion = false;
            listSuggestion.Show();
        }

        private void hideSuggestion()
        {
            showListSuggestion = true;
            listSuggestion.Hide();
        }

        private void listVideoHost_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListView list = sender as ListView;
            if (list.SelectedIndices.Count > 0)
            {
                selectedVideoHost = (VideoHosts)list.SelectedIndices[0];
                logo.Image = listLogo[(int)selectedVideoHost];
            }
        }

        private void listVideoHost_MouseClick(object sender, MouseEventArgs e)
        {
            hideDrop();
            textSearch.Focus();
        }

        private void listVideoHost_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                hideDrop();
                textSearch.Focus();
            }
        }

        private void UserControl1_MouseClick(object sender, MouseEventArgs e)
        {
            hideDrop();
            hideSuggestion();
            this.Focus();
        }
        #endregion

        #region events handling
        public event EventHandler<SearchFinishedEventArgs> SearchFinished = null;
        public event EventHandler SearchStarted = null;
        public event ProgressChangedEventHandler DownloadProgressChanged = null;
        public event RunWorkerCompletedEventHandler DownloadCompleted = null;
        public event RunWorkerCompletedEventHandler ExtractVideoURLCompleted = null;
        private void RaiseEventSearchStarted(EventArgs e)
        {
            if (this.SearchStarted != null)
            {
                this.SearchStarted(this, e);
            }
        }
        private void RaiseEventSearchFinished(SearchFinishedEventArgs e)
        {
            IsSearching = false;
            if (this.SearchFinished != null)
            {
                this.SearchFinished(this, e);
            }
        }
        #endregion

        #region check internet access
        private bool HasInternetAccess()
        {
            using (Ping ping = new Ping())
            {
                try
                {
                    PingReply rep = ping.Send(@"www.yahoo.com", 3000);
                    if (rep.Status != IPStatus.Success)
                    {
                        return false;
                    }
                }
                catch (System.Exception ex)
                {
                    return false;
                }
                return true;
            }
        }
        #endregion

        #region exposed properties
        private static WebException _lastWebException;
        private static int _requesttimeout = 10 * 1000;

        [Category("Connection Setting")]
        [Description("Set Request Timeout in ms")]
        public static int RequestTimeOut
        {
            get
            {
                return _requesttimeout;
            }
            set
            {
                _requesttimeout = value;
            }
        }

        public static WebException LastWebException
        {
            get
            {
                return _lastWebException;
            }
            set
            {
                _lastWebException = value;
            }
        }

        public double DownloadSpeed
        {
            get
            {
                if (downloader == null)
                {
                    return 0;
                }
                return downloader.DownloadSpeed;
            }
        }

        public long ETA
        {
            get
            {
                if (downloader == null)
                {
                    return 0;
                }
                return downloader.ETA;
            }
        }

        public DownloadStatus DownloadStatus
        {
            get
            {
                if (downloader == null)
                {
                    return PBKVideo.DownloadStatus.None;
                }
                return downloader.DownloadStatus;
            }
        }

        [Category("Style")]
        [Description("Set font in searchbox")]
        public Font TextSearchFont
        {
            get
            {
                return textSearch.Font;
            }
            set
            {
                textSearch.Font = value;
            }
        }

        [Category("Style")]
        [Description("Set background color of panel search")]
        public Color PanelSearchBackgroundColor
        {
            get
            {
                return panel1.BackColor;
            }
            set
            {
                panel1.BackColor = value;
            }
        }

        [Category("Style")]
        [Description("Set background color of text search box")]
        public Color TextSearchBackgroundColor
        {
            get
            {
                return textSearch.BackColor;
            }
            set
            {
                textSearch.BackColor = value;
            }
        }
        #endregion

        #region json deserializer
        private void DeserializeYoutubeJSON(string jsonSource)
        {
            //System.IO.File.WriteAllText(@"D:\testYoutube.txt", jsonSource);
            YoutubeVideo yutub = JsonConvert.DeserializeObject<YoutubeVideo>(jsonSource);
            SearchResult res = null;

            //System.IO.File.WriteAllText(@"D:\wedhusgembel888.txt",jsonSource);
            if (yutub.data == null)
            {
                RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.EMPTY_RESULT, null));
            }
            else
                if (yutub.data.Items == null || yutub.data.Items.Count == 0)
                {
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.NO_MORE_RESULTS, null));
                }
                else
                {
                    res = new SearchResult(yutub.data.TotalItems, jsonSource, yutub.data.ItemsPerPage, curpage);
                    foreach (PBKVideo.Item item in yutub.data.Items)
                    {
                        //MessageBox.Show(item.Title + " " + item.Uploader + " " + item.Description);
                        string embed = @"http://www.youtube.com/v/" + item.Id;
                        res.Videos.Add(new Video(item.Title, item.Uploader, item.Description, item.thumbnail.SqDefault, @"https://www.youtube.com/watch?v=" + item.Id, embed));
                    }
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.OK, res));
                }
        }

        private void DeserializeVimeoJSON(string jsonSource)
        {
            //System.IO.File.WriteAllText(@"D:\testVimeo.txt", jsonSource);
            VimeoVideo vimeo = JsonConvert.DeserializeObject<VimeoVideo>(jsonSource);
            SearchResult res = null;
            //System.IO.File.WriteAllText(@"D:\testPBKVimeo.txt", jsonSource);
            
            if (vimeo.Videos == null)
            {
                RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.EMPTY_RESULT, null));
            }
            else
                if (vimeo.Videos.Video == null || vimeo.Videos.Video.Count == 0)
                {
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.NO_MORE_RESULTS, null));
                }
                else
                {
                    res = new SearchResult(vimeo.Videos.Total, jsonSource, vimeo.Videos.PerPage, curpage);
                    foreach (PBKVideo.VideoVimeo item in vimeo.Videos.Video)
                    {
                        string embed = @"http://player.vimeo.com/video/" + item.Id;
                        Video vid = new Video(item.Title, item.Owner.Display_Name, item.Description, item.Thumbnails.Thumbnail[0].Content, @"http://vimeo.com/" + item.Id, embed);
                        res.Videos.Add(vid);
                    }
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.OK, res));
                }
        }

        private void DeserializeDailymotionJSON(string jsonSource)
        {
            //System.IO.File.WriteAllText(@"D:\testDailymotion.txt", jsonSource);
            DailymotionVideo dm = JsonConvert.DeserializeObject<DailymotionVideo>(jsonSource);
            SearchResult res = null;

            if (dm.list == null)
            {
                RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.EMPTY_RESULT, null));
            }
            else
                if (dm.list.Count == 0)
                {
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.NO_MORE_RESULTS, null));
                } 
                else
                {
                    res = new SearchResult(dm.Total, jsonSource, dm.Limit, dm.Page);
                    foreach (PBKVideo.ListItem item in dm.list)
                    {
                        string embed = @"http://www.dailymotion.com/embed/video/" + item.Id;
                        Video vid = new Video(item.Title, item.Owner, item.Description, item.Thumbnail_180_URL, @"http://www.dailymotion.com/video/" + item.Id, embed);
                        res.Videos.Add(vid);
                    }
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.OK, res));
                }
        }

        #region XMLFormatter
        //source: http://www.codeproject.com/Articles/17150/Formatting-XML-A-Code-snippet
        private string FormatXml(string sUnformattedXml)
        {
            //load unformatted xml into a dom
            XmlDocument xd = new XmlDocument();
            xd.LoadXml(sUnformattedXml);

            //will hold formatted xml
            StringBuilder sb = new StringBuilder();

            //pumps the formatted xml into the StringBuilder above
            StringWriter sw = new StringWriter(sb);

            //does the formatting
            XmlTextWriter xtw = null;

            try
            {
                //point the xtw at the StringWriter
                xtw = new XmlTextWriter(sw);

                //we want the output formatted
                xtw.Formatting = System.Xml.Formatting.Indented;

                //get the dom to dump its contents into the xtw 
                xd.WriteTo(xtw);
            }
            finally
            {
                //clean up even if error
                if (xtw != null)
                    xtw.Close();
            }

            //return the formatted xml
            return sb.ToString();
        }
        #endregion

        private void DeserializeVeohJSON(string jsonSource)
        {
            jsonSource = FormatXml(jsonSource);
            //System.IO.File.WriteAllText(@"D:\testtestVEOH.txt", jsonSource);
            VeohVideo veoh = null;
            using (TextReader reader = new StringReader(jsonSource))
            {
                XmlSerializer deserializer = new XmlSerializer(typeof(VeohVideo));
                veoh = deserializer.Deserialize(reader) as VeohVideo;
            }
            SearchResult res = null;

            if (veoh == null || veoh.Status == null || veoh.Status.ToLower() != "ok")
            {
                RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.EMPTY_RESULT, null));
            }
            else
                if (veoh.VideoList.PerPage == 0)
                {
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.NO_MORE_RESULTS, null));
                }
                else
                {
                    res = new SearchResult(veoh.VideoList.Total, jsonSource, veoh.VideoList.PerPage, curpage);
                    foreach (PBKVideo.VideoVeoh item in veoh.VideoList.Videos)
                    {
                        string embed = String.Format(@"http://www.veoh.com/static/swf/veoh/SPL.swf?version=AFrontend.5.7.0.1446&permalinkId={0}&player=videodetailsembedded&videoAutoPlay=0&id=anonymous", item.PermalinkID);
                        Video vid = new Video(item.Title, item.Uploader, item.Description, item.Thumb, @"http://www.veoh.com/watch/" + item.PermalinkID, embed);
                        res.Videos.Add(vid);
                    }
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.OK, res));
                }
        }
        #endregion

        public void CancelDownloadAsync()
        { 
            if (downloader != null)
            {
                downloader.CancelAsync();
            }
        }

        public void PauseDownload()
        {
            if (downloader != null)
            {
                downloader.Pause();
            }
        }

        public void ResumeDownload()
        {
            if (downloader != null)
            {
                downloader.Resume();
            }
        }

        public void AbortDownload()
        {
            if (downloader != null)
            {
                downloader.Abort();
            }
        }

        #region video search
        private void StartSearch()
        {
            if (String.IsNullOrEmpty(textSearch.Text))
            {
                return;
            }
            lastquery = textSearch.Text;
            GetSearchResultOnPage(1);
        }

        public void GetSearchResultOnPage(int page)
        {
            if (IsSearching || String.IsNullOrEmpty(textSearch.Text) || textSearch.Font == placeholderFontSearch)
            {
                return;
            }
            IsSearching = true;
            if (String.IsNullOrEmpty(lastquery))
            {
                lastquery = textSearch.Text;
            }
            curpage = page;
            switch (selectedVideoHost)
            {
                case VideoHosts.YOUTUBE:
                    page = (page - 1) * perpage + 1;
                    YoutubeSearch(page);
                    break;
                case VideoHosts.VIMEO:
                    VimeoSearch(page);
                    break;
                case VideoHosts.DAILYMOTION:
                    DailymotionSearch(page);
                    break;
                case VideoHosts.VEOH:
                    VeohSearch(page-1);
                    break;
            }
            textSearch.Text = lastquery;
            swSugg = 1;
        }

        private void RequestWebServiceFinished(IAsyncResult result)
        {
            RequestWebServiceState state = result.AsyncState as RequestWebServiceState;
            HttpWebRequest webRequest = state.Request;
            string jsonSource = @"";
            try {
                using (HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(result))
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        result.AsyncWaitHandle.WaitOne();
                        using (BufferedStream bs = new BufferedStream(stream))
                        {
                            using (StreamReader sr = new StreamReader(bs))
                            {
                                jsonSource = sr.ReadToEnd();
                            }
                        }
                    }
                    response.Close();
                }
            }
            catch (WebException wex)
            {
                if (!state.HasBeenAborted)
                {
                    state.HasBeenAborted = true;
                    if (reqeustWaitHandle != null)
                        reqeustWaitHandle.Unregister(result.AsyncWaitHandle);
                    _lastWebException = wex;
                    if (!HasInternetAccess())
                    {
                        RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.NO_INTERNET_CONNECTION, null));
                    }
                    else
                        if (wex.Status == WebExceptionStatus.Timeout)
                        {
                            RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.TIMEOUT, null));
                        }
                        else
                        {
                            RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.UNKNOWN_INTERNET_CONNECTION_ERROR, null));
                        }
                }
            }
            catch (Exception ex)
            {
                if (!state.HasBeenAborted)
                {
                    state.HasBeenAborted = true;
                    if (reqeustWaitHandle != null)
                        reqeustWaitHandle.Unregister(result.AsyncWaitHandle);
                    RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.UNKNOWN_ERROR, null));
                }
            }

            if (!String.IsNullOrEmpty(jsonSource))
            {
                System.IO.File.WriteAllText(@"D:\pbksayangsayang.txt", jsonSource);
                switch (state.VideoHost) {
                    case VideoHosts.YOUTUBE:
                        DeserializeYoutubeJSON(jsonSource);
                        break;
                    case VideoHosts.VIMEO:
                        DeserializeVimeoJSON(jsonSource);
                        break;
                    case VideoHosts.DAILYMOTION:
                        DeserializeDailymotionJSON(jsonSource);
                        break;
                    case VideoHosts.VEOH:
                        DeserializeVeohJSON(jsonSource);
                        break;
                }
            }
        }

        private void RequestWebServiceTimedOut(object state, bool timedOut)
        {
            if (timedOut)
            {
                RequestWebServiceState reqState = state as RequestWebServiceState;
                if (reqState != null)
                {
                    reqState.HasBeenAborted = true;
                    reqState.Request.Abort();
                    if (!HasInternetAccess())
                    {
                        RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.NO_INTERNET_CONNECTION, null));
                    }
                    else
                    {
                        RaiseEventSearchFinished(new SearchFinishedEventArgs(SearchStats.TIMEOUT, null));
                    }
                }
            }
        }

        private void RequestWebService(ref HttpWebRequest webRequest)
        {
            RequestWebServiceState state = new RequestWebServiceState(webRequest, selectedVideoHost, false);
            RaiseEventSearchStarted(EventArgs.Empty);
            IAsyncResult result = webRequest.BeginGetResponse(new AsyncCallback(RequestWebServiceFinished), state);
            reqeustWaitHandle = ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(RequestWebServiceTimedOut), state, _requesttimeout, true);
        }

        private void YoutubeSearch(int page)
        {
            string uri = String.Format(youtubeURI, lastquery, perpage, page);
            //MessageBox.Show("WOEEEEE1 " + uri);

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
            webRequest.Method = "GET";
            webRequest.Host = @"gdata.youtube.com";
            webRequest.Proxy = Helper.InitialProxy();
            webRequest.Timeout = _requesttimeout;
            webRequest.ReadWriteTimeout = _requesttimeout;
            webRequest.ProtocolVersion = HttpVersion.Version11;
            webRequest.KeepAlive = true;
            //webRequest.ServicePoint.Expect100Continue = false;
            webRequest.Accept = @"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            //webRequest.UserAgent = @"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0";
            webRequest.Headers.Add(HttpRequestHeader.AcceptLanguage, @"en-US,en;q=0.5");

            RequestWebService(ref webRequest);
        }

        private void DailymotionSearch(int page)
        {
            string uri = String.Format(dailymotionURI, lastquery, perpage, page);
            //MessageBox.Show("WOEEEEE1 " + uri);

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
            webRequest.Method = "GET";
            webRequest.Host = @"api.dailymotion.com";
            webRequest.Proxy = Helper.InitialProxy();
            webRequest.Timeout = _requesttimeout;
            webRequest.ReadWriteTimeout = _requesttimeout;
            webRequest.ProtocolVersion = HttpVersion.Version11;
            webRequest.KeepAlive = true;
            //webRequest.ServicePoint.Expect100Continue = false;
            webRequest.Accept = @"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            //webRequest.UserAgent = @"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0";
            webRequest.Headers.Add(HttpRequestHeader.AcceptLanguage, @"en-US,en;q=0.5");

            RequestWebService(ref webRequest);
        }

        private void VeohSearch(int page)
        {
            string uri = String.Format(veohURI, lastquery, perpage, page);
            //MessageBox.Show("WOEEEEE1 " + uri);

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
            webRequest.Method = "GET";
            webRequest.Host = @"www.veoh.com";
            webRequest.Proxy = Helper.InitialProxy();
            webRequest.Timeout = _requesttimeout;
            webRequest.ReadWriteTimeout = _requesttimeout;
            webRequest.ProtocolVersion = HttpVersion.Version11;
            webRequest.KeepAlive = true;
            //webRequest.ServicePoint.Expect100Continue = false;
            webRequest.Accept = @"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            //webRequest.UserAgent = @"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0";
            webRequest.Headers.Add(HttpRequestHeader.AcceptLanguage, @"en-US,en;q=0.5");

            RequestWebService(ref webRequest);
        }

        private void VimeoSearch(int page)
        {
            string uri = String.Format(vimeoURI, lastquery, perpage, page);
            Uri URI = new Uri(uri);

            string consumerKey = "df4aba8124cb88cafd3e1e64c8214f2a03df0338";
            string consumerSecret = "50ef24f8b2bff21019ae58417b330ccce1b81af1";
            string version = "1.0";
            string method = "HMAC-SHA1";

            string url, param;
            var oAuth = new OAuthBase();
            var nonce = oAuth.GenerateNonce();
            var timeStamp = oAuth.GenerateTimeStamp();

            HttpWebRequest webRequest = WebRequest.Create(uri) as HttpWebRequest;
            webRequest.Host = "vimeo.com";
            webRequest.Accept = "*/*";
            webRequest.Method = "GET";
            webRequest.Proxy = Helper.InitialProxy();
            webRequest.Timeout = _requesttimeout;
            webRequest.ReadWriteTimeout = _requesttimeout;
            webRequest.ProtocolVersion = HttpVersion.Version11;

            var signature = oAuth.GenerateSignature(URI, consumerKey,
            consumerSecret, string.Empty, string.Empty, "GET", timeStamp, nonce,
            OAuthBase.SignatureTypes.HMACSHA1, out url, out param);

            webRequest.Headers.Add(HttpRequestHeader.Authorization, "OAuth realm=\"\"," +
                "oauth_consumer_key=\"" + consumerKey + "\"," +
                "oauth_version=\"" + version + "\"," +
                "oauth_signature_method=\"" + method + "\"," +
                "oauth_timestamp=\"" + timeStamp + "\"," +
                "oauth_nonce=\"" + nonce + "\"," +
                "oauth_signature=\"" + signature + "\"");

            RequestWebService(ref webRequest);
        }
        #endregion

        #region suggestion engine
        private void SuggestingFinished(IAsyncResult result)
        {
            bool exception = false;
            String res = @"";
            suggSuccess = false;
            HttpWebRequest webRequest = result.AsyncState as HttpWebRequest;
            try
            {
                using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(result))
                {
                    using (Stream stream = webResponse.GetResponseStream())
                    {
                        result.AsyncWaitHandle.WaitOne();
                        IsSuggesting = false;
                        using (BufferedStream bs = new BufferedStream(stream))
                        {
                            using (StreamReader sr = new StreamReader(bs))
                            {
                                res = sr.ReadToEnd();
                            }
                        }
                    }
                    webResponse.Close();
                }
            }
            catch (System.Exception ex)
            {
                suggSuccess = false;
                exception = true;
                WebException wex = ex as WebException;
                if (wex != null && wex.Status != WebExceptionStatus.RequestCanceled)
                {
                    //MessageBox.Show(wex.Status.ToString());
                    _lastWebException = wex;
                }
            }

            if (!exception)
            {
                //System.IO.File.WriteAllText(@"D:\testPBK.txt", res);

                ProcessSuggestingFinished(res);
            }
        }

        private void ProcessSuggestingFinished(string res)
        {
            if (this.listSuggestion.InvokeRequired)
            {
                this.Invoke(new ProcessSuggestingFinishedCallback(ProcessSuggestingFinished), new object[] { res });
            }
            else
            {
                String ss = @"["""; //the separator is [" ... "
                listSuggestion.Items.Clear();
                HashSet<string> results = new HashSet<string>();
                for (int i = res.IndexOf(ss, res.IndexOf(ss, 0) + 2); i != -1 && i + 2 < res.Length; i = res.IndexOf(ss, i + 2))
                {
                    string temp = @"";
                    int j = i + 2;
                    for (; j < res.Length && res[j] != '\"'; j++)
                    {
                        temp += res[j];
                    }
                    results.Add(temp);
                    i = j;
                }
                listSuggestion.Items.AddRange(results.ToArray<String>());
                results = null;
                suggSuccess = true;
                IsSuggesting = false;
            }
        }

        private void SuggestingTimedOut(object state, bool timedOut)
        {
            if (timedOut)
            {
                HttpWebRequest req = state as HttpWebRequest;
                if (req != null)
                {
                    req.Abort();
                    _lastWebException = new WebException(@"Suggesting TimedOut.",WebExceptionStatus.Timeout);
                    suggSuccess = false;
                    IsSuggesting = false;
                }
            }
        }

        private void DoSuggesting()
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(suggestUrl + textSearch.Text);
            webRequest.Method = "GET";
            webRequest.Host = @"suggestqueries.google.com";
            webRequest.ProtocolVersion = HttpVersion.Version11;
            webRequest.Proxy = Helper.InitialProxy();
            webRequest.KeepAlive = true;
            //webRequest.ServicePoint.Expect100Continue = false;
            webRequest.Accept = @"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            webRequest.UserAgent = @"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0";
            webRequest.Headers.Add(HttpRequestHeader.AcceptLanguage, @"en-US,en;q=0.5");
            suggSuccess = false;
            IsSuggesting = true;

            IAsyncResult result = webRequest.BeginGetResponse(new AsyncCallback(SuggestingFinished), webRequest);
            ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(SuggestingTimedOut), webRequest, SUGGESTION_TIMEOUT, true);

            //loading.Show();
            
        }

        private void textSearch_TextChanged(object sender, EventArgs e)
        {
            if (swSugg == 1)
            {
                ++swSugg;
                return;
            }
            if (String.IsNullOrEmpty(textSearch.Text) || textSearch.Font.Style == FontStyle.Italic)
            {
                hideSuggestion();
                return;
            }
            hideDrop();
            showSuggestion();

            DoSuggesting();
        }
        #endregion

        private List<VideoQuality> YoutubeVideoExtract(string baseURL)
        {
            List<VideoQuality> videos = new List<VideoQuality>(); //ini yg akan di-return
            string html = Helper.DownloadWebPage(baseURL);

            //GET TITLE
            string str14 = Helper.GetTxtBtwn(html, "'VIDEO_TITLE': '", "'", 0);
            if (str14 == "") str14 = Helper.GetTxtBtwn(html, "\"title\" content=\"", "\"", 0);
            if (str14 == "") str14 = Helper.GetTxtBtwn(html, "&title=", "&", 0);
            str14 = str14.Replace(@"\", "").Replace("'", "&#39;").Replace("\"", "&quot;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("+", " ");
            string title = str14;

            //EXTRACT URL
            List<string> urls = new List<string>();
            string DataBlockStart = "\"url_encoded_fmt_stream_map\":\\s+\"(.+?)&";  // Marks start of Javascript Data Block

            html = Uri.UnescapeDataString(Regex.Match(html, DataBlockStart, RegexOptions.Singleline).Groups[1].ToString());

            string firstPatren = html.Substring(0, html.IndexOf('=') + 1);
            var matchs = Regex.Split(html, firstPatren);
            for (int i = 0; i < matchs.Length; i++)
                matchs[i] = firstPatren + matchs[i];
            foreach (var match in matchs)
            {
                if (!match.Contains("url=")) continue;

                string url = Helper.GetTxtBtwn(match, "url=", "\\u0026", 0);
                if (url == "") url = Helper.GetTxtBtwn(match, "url=", ",url", 0);
                if (url == "") url = Helper.GetTxtBtwn(match, "url=", "\",", 0);

                string sig = Helper.GetTxtBtwn(match, "sig=", "\\u0026", 0);
                if (sig == "") sig = Helper.GetTxtBtwn(match, "sig=", ",sig", 0);
                if (sig == "") sig = Helper.GetTxtBtwn(match, "sig=", "\",", 0);

                while ((url.EndsWith(",")) || (url.EndsWith(".")) || (url.EndsWith("\"")))
                    url = url.Remove(url.Length - 1, 1);

                while ((sig.EndsWith(",")) || (sig.EndsWith(".")) || (sig.EndsWith("\"")))
                    sig = sig.Remove(sig.Length - 1, 1);

                if (string.IsNullOrEmpty(url)) continue;
                if (!string.IsNullOrEmpty(sig))
                    url += "&signature=" + sig;
                urls.Add(url);
            }

            //PROCESS setiap url yang didapat
            foreach (string videourl in urls)
            {
                VideoQuality vid = new VideoQuality();
                vid.DownloadURL = videourl + "&title=" + title;
                vid = Helper.GetVideoInfo(vid);
                if (vid != null)
                    videos.Add(vid);
            }

            return videos;
        }

        private List<VideoQuality> VimeoVideoExtract(string baseURL)
        {
            List<VideoQuality> videourl = new List<VideoQuality>();
            string html = Helper.DownloadWebPage(baseURL);
            string jsonurl = HttpUtility.HtmlDecode(Helper.GetTxtBtwn(html, "data-config-url=\"", "\"", 0));
            string jsonres = @"";
            jsonres = Helper.DownloadWebPage(jsonurl);
            
            if (!String.IsNullOrEmpty(jsonres))
            {
                VimeoVideoExtract vim = JsonConvert.DeserializeObject<VimeoVideoExtract>(jsonres);
                if (vim.Request.Files.H264.Hd != null)
                {
                    VideoQuality vid = new VideoQuality();
                    vid.DownloadURL = vim.Request.Files.H264.Hd.Url;
                    vid = Helper.GetVideoInfo(vid);
                    if (vid != null)
                    {
                        videourl.Add(vid);
                    }
                }
                if (vim.Request.Files.H264.Mobile != null)
                {
                    VideoQuality vid = new VideoQuality();
                    vid.DownloadURL = vim.Request.Files.H264.Mobile.Url;
                    vid = Helper.GetVideoInfo(vid);
                    if (vid != null)
                    {
                        videourl.Add(vid);
                    }
                }
                if (vim.Request.Files.H264.Sd != null)
                {
                    VideoQuality vid = new VideoQuality();
                    vid.DownloadURL = vim.Request.Files.H264.Sd.Url;
                    vid = Helper.GetVideoInfo(vid);
                    if (vid != null)
                    {
                        videourl.Add(vid);
                    }
                }
            }

            return videourl;
        }

        private List<VideoQuality> VeohVideoExtract(string baseURL)
        {
            List<VideoQuality> videourl = new List<VideoQuality>();
            string html = Helper.DownloadWebPage(baseURL);
            string candurl = Helper.GetTxtBtwn(html, "\"previewUrl\":\"", "\"", 0);

            string resulturl = candurl.Replace(@"\", @"");
            VideoQuality vid = new VideoQuality();
            vid.DownloadURL = resulturl;
            vid = Helper.GetVideoInfo(vid);
            if (vid != null)
            {
                videourl.Add(vid);
            }

            return videourl;
        }

        private List<VideoQuality> DailymotionVideoExtract(string baseURL)
        {
            List<VideoQuality> videourl = new List<VideoQuality>();
            string html = Helper.DownloadWebPage(baseURL);
            string candurl = Helper.GetTxtBtwn(html, @"video_url%22%3A%22", @"%22%2C%22", 0);

            string resulturl = HttpUtility.UrlDecode(HttpUtility.UrlDecode(candurl));
            VideoQuality vid = new VideoQuality();
            vid.DownloadURL = resulturl;
            vid = Helper.GetVideoInfo(vid);
            if (vid != null)
            {
                videourl.Add(vid);
            }

            return videourl;
        }

        public void ExtractVideoURLs(Video vid)
        {
            backgroundWorker1 = new BackgroundWorker();
            backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            backgroundWorker1.RunWorkerCompleted += ExtractVideoURLCompleted;
            backgroundWorker1.RunWorkerAsync(vid);
        }

        public void DownloadVideo(string videourl, string path)
        { 
            if (downloader != null && downloader.IsBusy)
            {
                return;
            }
            //run download async
            string folder = Path.GetDirectoryName(path);
            string file = Path.GetFileName(path);
            downloader = new FileDownloader(videourl, folder, file);
            downloader.ProgressChanged += DownloadProgressChanged;
            downloader.RunWorkerCompleted += DownloadCompleted;

            downloader.RunWorkerAsync(); //seperate thread
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            Video vid = e.Argument as Video;
            string url = vid.URL;
            List<VideoQuality> videourls = null;
            if (!String.IsNullOrEmpty(url))
            {
                Uri uu = new Uri(url);
                if (uu.Host.ToLower().Contains("youtube"))
                {
                    videourls = YoutubeVideoExtract(url);
                }
                else
                    if (uu.Host.ToLower().Contains("vimeo"))
                    {
                        videourls = VimeoVideoExtract(url);
                    }
                    else
                        if (uu.Host.ToLower().Contains("veoh"))
                        {
                            videourls = VeohVideoExtract(url);
                        }
                        else
                            if (uu.Host.ToLower().Contains("dailymotion"))
                            {
                                videourls = DailymotionVideoExtract(url);
                            }
            }
            e.Result = videourls;
        }

    }
}
